package com.doyutu.seed.config;

import com.doyutu.seed.config.shiro.CustomSessionManager;
import com.doyutu.seed.config.shiro.LoginRealm;
import com.doyutu.seed.config.shiro.PathMatchesFilter;
import com.doyutu.seed.config.shiro.RedisSessionDAO;
import com.doyutu.seed.config.shiro.RolesOrFilter;
import java.util.HashMap;
import javax.servlet.Filter;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author DoyuTu
 * @version 0.0.1
 * spring-boot-project-seed
 */
@Configuration
public class ShiroConfig {

    @Bean
    public RedisSessionDAO redisSessionDAO() {
        return new RedisSessionDAO();
    }

    @Bean
    public LoginRealm loginRealm() {
        LoginRealm loginRealm = new LoginRealm();
        loginRealm.setCachingEnabled(false);
        loginRealm.setAuthorizationCachingEnabled(true);
        loginRealm.setAuthenticationCachingEnabled(true);
//        loginRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        return loginRealm;
    }

    @Bean
    public LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

//    @Bean
//    public MemoryConstrainedCacheManager memoryConstrainedCacheManager() {
//        return new MemoryConstrainedCacheManager();
//    }

    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
        // 加密算法名称
        matcher.setHashAlgorithmName("md5");
        // 加密次数
        matcher.setHashIterations(1);
        matcher.setStoredCredentialsHexEncoded(true);
        return matcher;
    }

    @Bean
    public DefaultWebSessionManager sessionManager() {
        CustomSessionManager sessionManager = new CustomSessionManager();
        sessionManager.setSessionDAO(redisSessionDAO());
        sessionManager.setSessionValidationInterval(3600000*12);
        sessionManager.setGlobalSessionTimeout(3600000*12);
        sessionManager.setDeleteInvalidSessions(true);
        sessionManager.setSessionValidationSchedulerEnabled(true);
        return sessionManager;
    }

    @Bean
    public DefaultWebSecurityManager securityManager(CacheManager shiroRedisCacheManager) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        //realm
        securityManager.setRealm(loginRealm());
        //remember mecookieRememberMeManager
        securityManager.setRememberMeManager(rememberMeManager());
        securityManager.setSessionManager(sessionManager());
        //cache manager
        securityManager.setCacheManager(shiroRedisCacheManager);
        return securityManager;
    }


    /**
     * Shiro注解支持:@RequiresPermissions @RequiresRoles
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

    /**
     * 或pom添加spring-boot-starter-aop且spring.aop.proxy-target-class=true
     */
    @Bean
    @ConditionalOnMissingBean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
        defaultAAP.setProxyTargetClass(true);
        return defaultAAP;
    }

    /**
     * 自动登录
     */
    @Bean
    public SimpleCookie rememberMeCookie() {
        SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
        simpleCookie.setHttpOnly(true);
        // 7天
        simpleCookie.setMaxAge(7 * 24 * 60 * 60);
        //必须https
//        simpleCookie.setSecure(true);
        return simpleCookie;
    }

    @Bean
    public CookieRememberMeManager rememberMeManager() {
        CookieRememberMeManager rememberMeManager = new CookieRememberMeManager();
        rememberMeManager.setCookie(rememberMeCookie());
        rememberMeManager.setCipherKey(Base64.decode("0qltNS@v&auEc0R16^$@!8Podg#DPaPO"));
        return rememberMeManager;
    }

    @Bean
    public PathMatchesFilter pathMatchesFilter() {
        return new PathMatchesFilter();
    }

    @Bean
    public RolesOrFilter rolesOrFilter() {
        return new RolesOrFilter();
    }

    /**
     * shiro内置过滤器：
     * anon        任何
     * authBasic   httpBasic
     * authc       已认证
     * user        已登录
     * logout      退出
     * <p>
     * perms       权限,允许：perms["user:delete", "user:update"]
     * roles       角色,同时具备：roles["admin", "manager"]
     * ssl         协议
     * port        指定端口
     * 自定义过滤器：
     * pathMatches URL
     * rolesOr
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean();
        filterFactoryBean.setSecurityManager(securityManager);
        filterFactoryBean.setLoginUrl("login.html");
        filterFactoryBean.setSuccessUrl("index.html");
        filterFactoryBean.setUnauthorizedUrl("error/403.html");
        HashMap<String, Filter> filters = new HashMap<>(4);
        filters.put("pathMatches", pathMatchesFilter());
        filters.put("rolesOr", rolesOrFilter());
        filterFactoryBean.setFilters(filters);
        HashMap<String, String> filterChainDefinitionMap = new HashMap<>(12);
        filterChainDefinitionMap.put("/css/**", "anon");
        filterChainDefinitionMap.put("/login", "anon");
        filterChainDefinitionMap.put("/logout", "anon");
        filterChainDefinitionMap.put("/js/**", "anon");
        filterChainDefinitionMap.put("/img/**", "anon");
        filterChainDefinitionMap.put("/error/**", "anon");
        filterChainDefinitionMap.put("/druid/**", "anon");
        filterChainDefinitionMap.put("/manager/**", "pathMatches");
//        filterChainDefinitionMap.put("/manager/**", "rolesOr["admin","maanger"]");
        filterChainDefinitionMap.put("/**", "authc");
        filterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return filterFactoryBean;
    }

    @Bean
    public ShiroFilterChainDefinition shiroFilterChainDefinition() {
        //不需要在此处配置权限页面,因为上面的ShiroFilterFactoryBean已经配置过,但是此处必须存在,因为shiro-spring-boot-web-starter或查找此Bean,没有会报错
        return new DefaultShiroFilterChainDefinition();
    }

}
